#include "Shader.h"
#include "Core/ResourceControl.h"

using namespace std;
SakuraAge::Shader::Shader(){
}
SakuraAge::Shader::Shader(const string& Path, bool useEBO, ObjRef<Texture> t){
  this->useEBO = useEBO;

  int success;
  char infoLog[512];
  string vert = ResourceControl::LoadStringFromResource(string("Shaders/") + Path + ".vert");
  string frag = ResourceControl::LoadStringFromResource(string("Shaders/") + Path + ".frag");
  unsigned vShd, fShd;
  vShd = glCreateShader(GL_VERTEX_SHADER);
  fShd = glCreateShader(GL_FRAGMENT_SHADER);

  const char* vStr = vert.c_str();
  const char* fStr = frag.c_str();
  GLint vSize = vert.size() - 1, fSize = frag.size() - 1;
  glShaderSource(vShd, 1, &vStr, &vSize);
  glShaderSource(fShd, 1, &fStr, &fSize);

  glCompileShader(vShd);
  glGetShaderiv(vShd, GL_COMPILE_STATUS, &success);
  if (!success) {
    glGetShaderInfoLog(vShd, 512, NULL, infoLog);
    DebugLog("\nVertexShader:\n");
    DebugLog(vStr);
    DebugLog("\n[VertexShader]CompileError:\n");
    DebugLog(infoLog);
    DebugLog(std::endl);
  }

  glCompileShader(fShd);
  glGetShaderiv(fShd, GL_COMPILE_STATUS, &success);
  if (!success) {
    DebugLog("\nFragmentShader:\n");
    DebugLog(fStr);
    glGetShaderInfoLog(fShd, 512, NULL, infoLog);
    DebugLog("\n[FragmentShader]CompileError:\n");
    DebugLog(infoLog);
    DebugLog(std::endl);
  }
  shaderProgram = glCreateProgram();
  glAttachShader(shaderProgram, vShd);
  glAttachShader(shaderProgram, fShd);
  glLinkProgram(shaderProgram);
  glDeleteShader(vShd);
  glDeleteShader(fShd);

  glGenBuffers(1, &VBO);
  glBindBuffer(GL_ARRAY_BUFFER, VBO);

  glGenVertexArrays(1, &VAO);

  if (useEBO) {
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ARRAY_BUFFER, EBO);
  }
  texture = t;
}

void SakuraAge::Shader::Render(float* vertices, unsigned size,
                                     int* elements, unsigned int points) const{
  glUseProgram(shaderProgram);

  if (texture != nullptr) {
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), vertices,
                 GL_DYNAMIC_DRAW);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), NULL);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
                          (void*)(2 * sizeof(float)));
    glEnableVertexAttribArray(1);
    texture->Bind();
  } else {
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), vertices,
                 GL_DYNAMIC_DRAW);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), NULL);
    glEnableVertexAttribArray(0);
  }

  glBindVertexArray(VAO);
  glDrawArrays(GL_TRIANGLES, 0, points);
}

void SakuraAge::Shader::SetFloat(const GLchar* name, GLfloat f) const{
  glUseProgram(shaderProgram);
  glUniform1f(glGetUniformLocation(shaderProgram, name), f);
}

void SakuraAge::Shader::SetInt(const GLchar* name, GLint i) const{
  glUseProgram(shaderProgram);
  glUniform1i(glGetUniformLocation(shaderProgram, name), i);
}

void SakuraAge::Shader::SetVector2(const GLchar* name, GLfloat x,
                                         GLfloat y) const{
  glUseProgram(shaderProgram);
  glUniform2f(glGetUniformLocation(shaderProgram, name), x, y);
}

void SakuraAge::Shader::SetColor(const GLchar* name, float r, float g,
                                       float b, float a) const{
  glUseProgram(shaderProgram);
  glUniform4f(glGetUniformLocation(shaderProgram, name), r, g, b, a);
}

void SakuraAge::Shader::Serialize(SerializedStream& ss) const{
  ss & (ObjectRef&)texture;
}
